package com.mysoft.aiot.plugin.feign;

import com.google.common.base.Strings;
import com.mysoft.aiot.plugin.feign.git.GitRepository;
import com.mysoft.aiot.plugin.feign.model.FeignClass;
import com.mysoft.aiot.plugin.feign.model.FeignClientClass;
import com.mysoft.aiot.plugin.feign.model.FeignText;
import com.mysoft.aiot.plugin.feign.util.DynamicClasspath;
import freemarker.template.TemplateException;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.*;
import org.apache.maven.project.MavenProject;
import org.eclipse.jgit.api.errors.GitAPIException;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

/**
 * AutoGenerator
 *
 */
@Mojo(name = "generate",
        requiresDependencyResolution = ResolutionScope.COMPILE,
        defaultPhase = LifecyclePhase.COMPILE,
        instantiationStrategy = InstantiationStrategy.SINGLETON
)
public class AutoGenerator extends AbstractMojo {

  @Parameter(defaultValue = "${project}", readonly = true, required = true)
  private MavenProject project;

  @Parameter(defaultValue = "${scanPackage}", readonly = true)
  private String scanPackage;

  @Parameter(defaultValue = "${outputPackage}", readonly = true)
  private String outputPackage;

  @Parameter(defaultValue = "${urlPropertyName}", readonly = true)
  private String urlPropertyName;

  @Parameter(defaultValue = "${gitUrl}", readonly = true)
  private String gitUrl;

  @Parameter(defaultValue = "${moduleName}", readonly = true)
  private String moduleName;

  @Parameter(defaultValue = "${includeBranch}", readonly = true)
  private String includeBranch;

  private static final String FEIGN_GENERATOR_PLUGIN = "feign-generator-plugin";
  private static final String JAR = ".jar";

  private boolean isFinished = false;

  @Override
  public void execute() throws MojoExecutionException, MojoFailureException {
    if (isFinished) {
      // 不知道为啥插件会被执行两次
      return;
    }

    if (Strings.isNullOrEmpty(scanPackage)) {
      scanPackage = project.getGroupId() + "." + project.getArtifactId();
    }

    if (Strings.isNullOrEmpty(outputPackage)) {
      outputPackage = project.getGroupId() + ".idl." + project.getArtifactId() + ".service";
    }

    if (Strings.isNullOrEmpty(urlPropertyName)) {
      urlPropertyName = "params.service." + project.getArtifactId();
    }

    Environment.log = getLog();
    Environment.mavenProject = project;
    Environment.scanPackage = scanPackage;
    Environment.outputPackage = outputPackage;
    Environment.urlPropertyName = urlPropertyName;

    try {

      List<String> classpathElements = project.getCompileClasspathElements();

      String classes = null;

      DynamicClasspath dynamicClasspath = DynamicClasspath.getInstance();

      for (String classpathElement : classpathElements) {
        try {
          if (classpathElement.contains(FEIGN_GENERATOR_PLUGIN)) {
            continue;
          }
          if (classpathElement.endsWith(JAR)) {
            dynamicClasspath.loadJar(classpathElement);
          } else {
            classes = classpathElement;
          }
        } catch (Exception e) {
          getLog().error(e);
        }
      }

      // 加载classes
      if (classes != null) {
        dynamicClasspath.loadDir(classes);
      }

      // 扫描所有controller
      FeignClass feignClass = scanFeignClass(scanPackage, dynamicClasspath);

      save(feignClass);
    } catch (Exception e) {
      getLog().error(e);
    }

    isFinished = true;
  }

  private FeignClass scanFeignClass(String scanPackage, DynamicClasspath dynamicClasspath) throws IOException {

    FeignClientScanner scanner = new FeignClientScanner(dynamicClasspath);

    List<FeignClientClass> list = scanner.scan(scanPackage);

    FeignClass feignClass = new FeignClass();
    feignClass.setFeignClientClasses(list);

    feignClass.setServiceName(project.getArtifactId());
    feignClass.setDescription(project.getDescription());
    feignClass.setVersion(project.getVersion());

    String groupId = project.getGroupId();
    if (groupId == null) {
      groupId = project.getParent().getGroupId();
    }
    feignClass.setGroupId(groupId);

    return feignClass;
  }

  private void save(FeignClass feignClass) throws IOException, TemplateException {
    List<FeignText> pathAndFileContents = new ArrayList<>();

    List<FeignClientClass> feignClientClasses = feignClass.getFeignClientClasses();

    pathAndFileContents.addAll(ClassResolver.resolveFeignClientClassList(feignClientClasses));


    if (!Strings.isNullOrEmpty(gitUrl)) {
      saveToGit(pathAndFileContents);
    } else {
      saveLocal(pathAndFileContents);
    }
  }

  private void saveLocal(List<FeignText> pathAndFileContents) throws IOException {
    for (FeignText text : pathAndFileContents) {
      File file = new File(project.getBuild().getOutputDirectory() + "/" + Environment.outputDirectoryName + "/" + text.getFileName());
      getLog().info("Generating " + text.getClazz() + " to " + file.getAbsolutePath());
      File dir = new File(file.getParent());
      if (!dir.exists()) {
        dir.mkdirs();
      }

      if (!file.exists()) {
        file.createNewFile();
      }

      try (FileWriter fw = new FileWriter(file.getAbsoluteFile());
           BufferedWriter bw = new BufferedWriter(fw)) {
        bw.write(text.getText());
      }
    }
  }

  private void saveToGit(List<FeignText> pathAndFileContents) {
    GitRepository gr = new GitRepository(gitUrl, this.moduleName);
    try {
      String targetBranch = GitRepository.getCurrentBranch(project.getBasedir().getAbsolutePath());
      if (Strings.isNullOrEmpty(targetBranch)) {
        getLog().info("Cannot retrieve current branch");
        return;
      }

      if (!Strings.isNullOrEmpty(this.includeBranch)) {
        // 用正则过滤分支
        if (!Pattern.matches(this.includeBranch, targetBranch)) {
          getLog().info("The branch " + targetBranch + " was skipped due to filter: " + this.includeBranch);
          return;
        }
      }

      gr.commit(targetBranch, pathAndFileContents);
    } catch (GitAPIException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}
